package loader

import (
	"errors"
	"fmt"
	"game/game-design/loader/consttype"
	"game/game-design/loader/meta"
	"strings"
)

type DesignFile struct {
	Name           string                         //文件名，像excel中多sheet的情况，文件名=原文件名_sheet名，此名字带有文件后缀
	ClassName      string                         //导出的类名
	FileName       string                         //文件名，用在生成代码的模板中
	Macros         []string                       //指令定义
	Fields         []*DesignField                 //字段
	FieldsMap      map[string]*DesignField        //字段名字的映射
	Datas          [][]string                     //数据
	ExportType     consttype.ExportType           //导出方式
	DesignDefine   *DesignDefine                  //配置格式的定义参数
	OutputNameType string                         //输出的文件名方式，用文件名作为文件名，或者用类名作为文件名
	Indexes        []*meta.IndexMeta              //索引列表
	Collectors     []*meta.CollectorMeta          //分组索引列表
	Dictionary     *meta.DictionaryMeta           //字典描述元数据
	Dictionaries   []*meta.DictionaryData         //字典列表
	Langs          map[string]*meta.LangMeta      //多语言定义的元数据
	LangMerges     map[string]*meta.LangMergeMeta //多语言定义的元数据
}

type DesignField struct {
	name    string //字段名
	form    string //字段类型
	tag     string //字段标签
	comment string //字段备注
	col     int    //字段所在的列
}

func (f *DesignField) Name() string {
	return f.name
}
func (f *DesignField) Form() string {
	return f.form
}
func (f *DesignField) Tag() string {
	return f.tag
}
func (f *DesignField) Comment() string {
	return f.comment
}
func (f *DesignField) Col() int {
	return f.col
}

func NewDesignFile(fileName string, rows [][]string, designDefine *DesignDefine) (*DesignFile, error) {

	fileName = strings.Replace(fileName, "\\", "", -1)
	fileName = strings.Replace(fileName, "/", "", -1)
	fileName = strings.Replace(fileName, ":", "", -1)
	fileName = strings.Replace(fileName, "*", "", -1)
	fileName = strings.Replace(fileName, "?", "", -1)
	fileName = strings.Replace(fileName, "\"", "", -1)
	fileName = strings.Replace(fileName, "<", "", -1)
	fileName = strings.Replace(fileName, ">", "", -1)
	fileName = strings.Replace(fileName, "|", "", -1)

	var dFile DesignFile
	dFile.Name = fileName
	dotIndex := strings.LastIndex(fileName, ".")
	if dotIndex >= 0 {
		dFile.FileName = fileName[:dotIndex]
	}

	firstRowNum, err := dFile.determineFirstRowNum(rows)
	if err != nil {
		return nil, err
	}

	dFile.DesignDefine = designDefine
	macros := rows[firstRowNum]
	dFile.FillMacro(macros)

	nameRow := rows[firstRowNum+designDefine.NameOffset]
	typeRow := rows[firstRowNum+designDefine.TypeOffset]
	tagRow := rows[firstRowNum+designDefine.TagOffset]
	commentRow := rows[firstRowNum+designDefine.CommentOffset]
	dFile.FillFieldAndData(nameRow, typeRow, tagRow, commentRow, rows[firstRowNum+5:])
	dFile.ensureIndexCollectorMetaCorrect()
	return &dFile, nil
}

func (d *DesignFile) determineFirstRowNum(rows [][]string) (int, error) {
	for i, row := range rows {
		if strings.HasPrefix(row[0], "#name") {
			return i, nil
		}
	}
	return 0, errors.New("未找到#name标识")
}

func (d *DesignFile) FillMacro(macros []string) {
	d.Macros = make([]string, 0)
	d.Indexes = make([]*meta.IndexMeta, 0)
	d.Collectors = make([]*meta.CollectorMeta, 0)
	d.Langs = make(map[string]*meta.LangMeta)
	d.LangMerges = make(map[string]*meta.LangMergeMeta)
	d.ExportType = consttype.BOTH
	for _, v := range macros {
		addMacro := true
		v = strings.TrimSpace(v)
		if strings.HasPrefix(v, "#name") {
			parts := strings.Split(v, " ")
			d.ClassName = parts[1]

		} else if strings.HasPrefix(v, "#export") {
			parts := strings.Split(v, " ")
			d.ExportType = d.DesignDefine.ExportType(parts[1])

		} else if strings.HasPrefix(v, "#index") {
			indexMeta := meta.NewIndexMeta(d.Name, v)
			d.Indexes = append(d.Indexes, indexMeta)

		} else if strings.HasPrefix(v, "#collector") {
			collectorMeta := meta.NewCollectorMeta(d.Name, v)
			d.Collectors = append(d.Collectors, collectorMeta)

		} else if strings.HasPrefix(v, "#const") || strings.HasPrefix(v, "#dictionary") {
			//TODO 可以考虑做成有多个字典定义
			d.Dictionary = meta.NewDictionaryMeta(v)
			//TODO 填充字典数据
		} else if strings.HasPrefix(v, "#langmerge") {
			langMeta, err := meta.NewLangMergeMeta(d.Name, v)
			if err != nil {
				panic("\033[31m[ERROR]\033[0m" + d.Name + "中的语言合并【" + v + "】指令错误（注意是否有中文空格）")
			}
			d.LangMerges[langMeta.Field()] = langMeta
			//语言类的宏不需要添加到导出表中
			addMacro = false
		} else if strings.HasPrefix(v, "#lang") {
			langMeta := meta.NewLangMeta(d.Name, v)
			d.Langs[langMeta.Field()] = langMeta
			//语言类的宏不需要添加到导出表中
			addMacro = false
		} else if v == "" {
		} else {
			fmt.Printf("这是什么？%v\n", v)
		}
		if addMacro && v != "" {
			d.Macros = append(d.Macros, v)
		}
	}
	if d.ClassName == "" {
		//TODO 抛异常
		panic("\033[31m[ERROR]\033[0m未定义类名")
	}
}

func (d *DesignFile) FillFieldAndData(names []string, types []string, tags []string, comments []string, rowDatas [][]string) {
	d.FieldsMap = make(map[string]*DesignField)
	d.Datas = make([][]string, 0, len(rowDatas))

	filterRowDatas := ensureNotAllEmpty(rowDatas)
	var colIdx int
	for i := range names {
		if names[i] == "" {
			continue
		}
		var field DesignField
		field.name = names[i]
		field.form = types[i]
		field.tag = tags[i]
		field.comment = comments[i]
		field.col = colIdx
		d.Fields = append(d.Fields, &field)
		d.FieldsMap[field.name] = &field
		colIdx += 1

		//数据是一列一列填充的
		for j, datas := range filterRowDatas {
			var fillDatas []string
			if j >= len(d.Datas) {
				fillDatas = make([]string, 0)
				d.Datas = append(d.Datas, fillDatas)
			} else {
				fillDatas = d.Datas[j]
			}
			var colData string
			if i >= len(datas) {
				colData = ""
			} else {
				colData = datas[i]
			}
			fillDatas = append(fillDatas, colData)
			d.Datas[j] = fillDatas
		}
	}
	d.loadDictionaryData()
}

func (d *DesignFile) AppendField(names string, types string, tags string, comments string) {
	var field DesignField
	field.name = names
	field.form = types
	field.tag = tags
	field.comment = comments
	field.col = len(d.Fields)
	d.Fields = append(d.Fields, &field)
	d.FieldsMap[field.name] = &field
}

func ensureNotAllEmpty(rowDatas [][]string) [][]string {
	datas := make([][]string, 0, len(rowDatas))
	for _, row := range rowDatas {
		if len(row) <= 0 || row[0] == "" {
			//首列为空则不导出
			continue
		}
		allEmpty := true
		for _, v := range row {
			if v != "" {
				allEmpty = false
				break
			}
		}
		if !allEmpty {
			datas = append(datas, row)
			// fmt.Println("FillData", row)
		}
	}
	return datas
}

func (d *DesignFile) FillField0(names []string, types []string, tags []string, comments []string) {
	size := len(names)
	if len(types) > size || len(tags) > size || len(comments) > size {
		panic("\033[31m[ERROR]\033[0m格式错误的配置表，未定义整齐的表头" + d.Name)
	}

	d.FieldsMap = make(map[string]*DesignField)
	var colIdx int
	for i := range names {
		if names[i] == "" {
			break
		}
		var field DesignField
		field.name = names[i]
		field.form = types[i]
		field.tag = tags[i]
		field.comment = comments[i]
		field.col = colIdx
		d.Fields = append(d.Fields, &field)
		d.FieldsMap[field.name] = &field
		colIdx += 1
	}
}

func (d *DesignFile) FillData(rowDatas [][]string) {
	d.Datas = make([][]string, 0, len(rowDatas))
	for _, row := range rowDatas {
		if len(row) <= 0 || row[0] == "" {
			//首列为空则不导出
			continue
		}
		allEmpty := true
		for _, v := range row {
			if v != "" {
				allEmpty = false
				break
			}
		}
		if !allEmpty {
			d.Datas = append(d.Datas, row)
			// fmt.Println("FillData", row)
		}
	}
	d.loadDictionaryData()
}

// 构建字典值
func (d *DesignFile) loadDictionaryData() {
	if d.Dictionary == nil {
		return
	}
	keyIndex := -1
	typeIndex := -1
	commentIndex := -1

	for i, v := range d.Fields {
		if v.Name() == d.Dictionary.Key() {
			keyIndex = i
		}
		if v.Name() == d.Dictionary.Form() {
			typeIndex = i
		}
		if v.Name() == d.Dictionary.Comment() {
			commentIndex = i
		}
	}
	if keyIndex < 0 || typeIndex < 0 {
		return
	}
	for _, v := range d.Datas {
		if commentIndex < 0 {
			dictionaryData := meta.OfDictionaryData(v[keyIndex], v[typeIndex], "")
			d.Dictionaries = append(d.Dictionaries, dictionaryData)
		} else {
			dictionaryData := meta.OfDictionaryData(v[keyIndex], v[typeIndex], v[commentIndex])
			d.Dictionaries = append(d.Dictionaries, dictionaryData)
		}
	}
}

// 获取文件输出时使用的名字
func (d *DesignFile) GetOutputName() string {
	if d.OutputNameType == "useFileName" {
		return d.Name
	} else {
		return d.ClassName
	}
}

// 文件是否需要导出
func (d *DesignFile) FileNeedExport(expectTag string) bool {
	expectTag0 := d.DesignDefine.ExportType(expectTag)
	return needExport(d.ExportType, expectTag0)
}

// 字段是否需要导出
func (d *DesignFile) FieldNeedExport(defineTag string, expectTag string) bool {
	defineTag0 := d.DesignDefine.ExportType(defineTag)
	expectTag0 := d.DesignDefine.ExportType(expectTag)
	return needExport(defineTag0, expectTag0)
}

// defineTag 定义标签
// expectTag 期望标签
func needExport(defineTag consttype.ExportType, expectTag consttype.ExportType) bool {
	if defineTag == consttype.NONE {
		return false
	}
	needExport := defineTag == consttype.BOTH || expectTag == consttype.BOTH || defineTag == expectTag
	return needExport
}

// 确保#index和#collector的定义正确
func (d *DesignFile) ensureIndexCollectorMetaCorrect() {
	for _, indexMeta := range d.Indexes {
		for _, fieldName := range indexMeta.Fields() {
			designField, ok := d.FieldsMap[fieldName]
			if !ok {
				panic(fmt.Sprintf("\033[31m[ERROR]\033[0m%s文件中#index定义未找到%s列（注意大小写）", d.Name, fieldName))
			}
			declareType := d.DesignDefine.ExportType(designField.tag)
			if declareType != d.ExportType && declareType != consttype.BOTH {
				panic(fmt.Sprintf("\033[31m[ERROR]\033[0m%s文件#index定义中的%s列必须是%s标签", d.Name, designField.Name(), d.ExportType))
			}
		}
	}
	for _, collectorMeta := range d.Collectors {
		for _, fieldName := range collectorMeta.Fields() {
			designField, ok := d.FieldsMap[fieldName]
			if !ok {
				panic(fmt.Sprintf("\033[31m[ERROR]\033[0m%s文件中#collector定义未找到%s列（注意大小写）", d.Name, fieldName))
			}
			declareType := d.DesignDefine.ExportType(designField.tag)
			if declareType != d.ExportType && declareType != consttype.BOTH {
				panic(fmt.Sprintf("\033[31m[ERROR]\033[0m%s文件#collector定义中的%s列必须是%s标签", d.Name, designField.Name(), d.ExportType))
			}
		}
	}
}

func (d *DesignFile) GetFirstIndexValue(rowData []string) string {
	indexMeta := d.Indexes[0]
	var indexValue string
	first := true
	for _, fieldName := range indexMeta.Fields() {
		fieldMeta := d.FieldsMap[fieldName]
		if first {
			indexValue += rowData[fieldMeta.col]
		} else {
			indexValue += "_" + rowData[fieldMeta.col]
		}
		first = false
	}
	return indexValue
}
